﻿#include "TcpClient.h"
#include "GlobalMsg.h"
#include "iostream"
#include "QMessageBox"




using namespace std;


TcpClient::TcpClient()
{
    //构建本地管理器
    tcpManger = new netManger_tcp(
        [=](QTcpSocket* socket){
            this->OnLocalTcpClientReadyRead(socket); //用于接收本地服务器给本地客户端的数据，最终发送到映射服务器进行发送TCP
        },
        [=](QTcpSocket* socket){
            this->OnLocalTcpClientDisconnect(socket); //用于本地客户端断开连接，最终将事件同步发送到映射服务器进行发送TCP
        });


    udpManger = new netManger_udp(
        [=](netManger_udp::socketMsg* sMsg){ //用于接收本地服务器给本地客户端的数据，最终发送到映射服务器进行发送UDP
            if(sMsg){
                this->OnLocalUdpClientReadyRead(sMsg->netHost,sMsg->netPort,sMsg->localPort,sMsg->socket->readAll());
            }
        });



}

TcpClient::~TcpClient()
{
    if(this->socket != nullptr) {
        this->socket->close();
        delete this->socket;
    }

    if(this->tcpManger){
        this->tcpManger->clear();
        delete this->tcpManger;
    }

    if(this->udpManger){
        this->udpManger->clear();
        delete this->udpManger;
    }
}


//判断是否有效
bool TcpClient::isValid()
{
    if(this->socket == nullptr){
        return false;
    }
    else{
        return this->socket->state() == QAbstractSocket::ConnectedState;
    }
}


//连接服务器
bool TcpClient::connectServer(QHostAddress host, uint16_t port, QString id)
{
    usrID = id; //保存ID信息
    if(this->socket == nullptr){
        this->socket = new QTcpSocket;
        QAbstractSocket::connect(this->socket,&QTcpSocket::readyRead,this,&TcpClient::OnTcpReadyRead); //绑定收到数据
        QAbstractSocket::connect(this->socket,&QTcpSocket::disconnected,this,&TcpClient::OnTcpDisConnect); //绑定断开连接


        //创建心跳计时器
        this->pantTimer = new QTimer;
        connect(this->pantTimer,&QTimer::timeout,[=]{
            this->noResponseNum++; //增加无响应次数，有响应将会自动重置
            this->socket->write(QByteArray().append('\0')); //发送心跳包，防止连接被路由回收

            if(this->noResponseNum > 3){ //三次心跳包无响应将会自动断开连接
                this->socket->close();
                this->pantTimer->stop();
                delete this->pantTimer; //回收心跳信息
            }

        });
        pantTimer->start(10000); //每隔10秒发送一次心跳
    }
    this->noResponseNum = 0; //清零无回应次数
    this->socket->connectToHost(host,port);
    return this->socket->waitForConnected(5000);
}



//远程添加映射
bool TcpClient::addProxy(QString name, bool isTcp, uint16_t listenPort, uint16_t proxyPort, bool persistent)
{
    if(this->isValid()){
        QByteArray t_sendData;
        t_sendData.append('\6'); //添加远程代理，数据结构: 映射名称 + 远程协议 + 远程端口 + 本地映射端口 + 持久化逻辑
        t_sendData.append(packManger::buildData({name.toLocal8Bit(),
                                                 QByteArray(isTcp ? "tcp" : "udp"),
                                                 QByteArray::number(listenPort),
                                                 QByteArray::number(proxyPort),
                                                 QByteArray(persistent ? "true" : "false")}));
        this->socket->write(t_sendData);
    }
    return false;
}


//获取映射列表
bool TcpClient::getProxyList()
{
    if(!this->isValid()) return false;
    QByteArray t_sendData;
    t_sendData.append('\2');
    this->socket->write(t_sendData);
    return true;
}


//移除映射
void TcpClient::removeProxy(uint16_t proxyPort)
{
    if(!this->isValid()) return;
    QByteArray t_sendData;
    t_sendData.append('\7');
    t_sendData.append(packManger::buildData({QByteArray::number(proxyPort)}));
    this->socket->write(t_sendData);
}


//客户端TCP收到数据
void TcpClient::OnTcpReadyRead()
{
    QByteArray t_data = this->socket->readAll(); //解析数据
    QByteArray t_reData = t_data.right(t_data.length() - 1);
    QByteArray t_sendData;

    if(t_data.at(0) == '\0'){ //呼吸包
        this->noResponseNum = 0; //清零无回应次数
        return;
    }
    else if(t_data.at(0) == '\1'){ //链接请求
        //封装用户ID到封包，发送给服务器，如果无效，服务器则会多次返回此信息
        t_sendData.clear();
        t_sendData.append('\1'); //发送返回验证数据,发送ID
        t_sendData.append(usrID.toLocal8Bit());
        this->socket->write(t_sendData);
    }
    else if(t_data.at(0) == '\2'){ //服务器完成用户的加入，开始获取持久数据列表
        this->getProxyList();
    }
    else if(t_data.at(0) == '\3'){ //服务器返回映射信息
        QString t_proxysStr =  QString(t_data.right(t_data.length() - 1));
        QStringList t_lines = t_proxysStr.split('\n');
        for(QList<QString>::iterator i = t_lines.begin(); i < t_lines.end(); i++){
            QStringList t_lineItems = i->split("&&&");
            if(t_lineItems.length() == 5){
                if(this->Fun_ClearList) this->Fun_ClearList;
                //QString name, bool isTcp, uint16_t serPort, uint16_t localPort, bool persistent
                if(this->Fun_addItem) this->Fun_addItem(
                        t_lineItems[0],
                        t_lineItems[1] == "tcp",
                        t_lineItems[2].toUInt(),
                        t_lineItems[3].toUInt(),
                        t_lineItems[4] == "persistent");

            }
        }
    }
    else if(t_data.at(0) == '\4'){ //TCP用户进入，数据结构: 客户标记 + 映射端口标记
        QList<QByteArray> t_msgs = packManger::parserData(t_reData);
        if(t_msgs.length() != 2) return;
        tcpManger->event_connect(QString(t_msgs[0]),t_msgs[1].toUInt()); //激发连接事件，自动添加本地用

    }
    else if(t_data.at(0) == '\5'){ //TCP数据到达，数据结构: 客户标记 + 映射端口标记 + 转发数据
        QList<QByteArray> t_msgs = packManger::parserData(t_reData);
        if(t_msgs.length() != 3) return;
        if(!tcpManger->event_readyRead(QString(t_msgs[0]),t_msgs[2])){ //判断是否能够转发成功，不成功则尝试加入成员后再次转发
            tcpManger->event_connect(QString(t_msgs[0]),t_msgs[1].toUInt()); //再次尝试加入连接
            tcpManger->event_readyRead(QString(t_msgs[0]),t_msgs[2]);
        }
    }
    else if(t_data.at(0) == '\6'){ //TCP用户断开，数据结构: 客户标记
        QList<QByteArray> t_msgs = packManger::parserData(t_reData);
        if(t_msgs.length() != 1) return;
        tcpManger->event_disConnect(QString(t_msgs[0]));
    }
    else if(t_data.at(0) == '\7'){ //UDP数据到达，数据结构: 外网用户Host + 外网用户Port + 映射端口标记 + 转发数据
        QList<QByteArray> t_msgs = packManger::parserData(t_reData);
        if(t_msgs.length() != 4) return;
        udpManger->event_readyRead(QString(t_msgs[0]), t_msgs[1].toUInt(),t_msgs[2].toUInt(),t_msgs[3]); //UDP转发数据
    }


}

//客户端断开连接
void TcpClient::OnTcpDisConnect()
{
    tcpManger->clear();
    udpManger->clear();

    if(Fun_disAppConnect != nullptr) this->Fun_disAppConnect(); //执行外部断开连接方法

}


//本地TCP客户端收到数据，将转发给映射服务器，映射服务器再转发给外网客户端
void TcpClient::OnLocalTcpClientReadyRead(QTcpSocket *socket)
{
    QString t_userSign = tcpManger->getServerAddress(socket);
    if(t_userSign.isEmpty()) return;
    QByteArray t_sendData;

    t_sendData.append('\3'); //TCP本地服务器返回数据，数据结构: 客户标记 + 本地映射端口 + 转发数据

    //注意：这里使用的peerPort为本地连接的服务器端口，若是localPort则是当前Socket监视绑定的端口
    t_sendData.append(packManger::buildData({t_userSign.toLocal8Bit(),QByteArray::number(socket->peerPort()),socket->readAll()}));
    this->socket->write(t_sendData);

}


//本地TCP客户端断开连接
void TcpClient::OnLocalTcpClientDisconnect(QTcpSocket *socket)
{
    QString t_userSign = tcpManger->getServerAddress(socket);
    if(t_userSign.isEmpty()) return;
    QByteArray t_sendData;

    t_sendData.append('\4'); //TCP本地服务器断开连接，数据结构: 客户标记 + 本地映射端口

    //注意：这里使用的peerPort为本地连接的服务器端口，若是localPort则是当前Socket监视绑定的端口
    t_sendData.append(packManger::buildData({t_userSign.toLocal8Bit(),QByteArray::number(socket->peerPort())}));
    this->socket->write(t_sendData);

    //回收这个对象
    tcpManger->removeUser(socket);
}


//本地UDP客户端收到数据，转发给服务器
void TcpClient::OnLocalUdpClientReadyRead(QString netHost, uint16_t netPort, uint16_t proxyPort, QByteArray data)
{
    QByteArray t_sendData;

    t_sendData.append('\5'); //UDP本地服务器返回数据，数据结构: 本地映射端口 + 远程Host + 远程Port + 转发数据
    t_sendData.append(packManger::buildData({QByteArray::number(proxyPort),
                                             netHost.toLocal8Bit(),
                                             QByteArray::number(netPort),
                                             socket->readAll()}));
    this->socket->write(t_sendData);
}
